/*
 * JoomlaClient.java
 *
 * Copyright (C) 2011 JoomBoss ( http://joomboss.com )
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 */
package com.joomboss;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

public class JoomlaClient implements IJoomlaClient{

	/**
	 * @param args
	 * @throws SAXException
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
	}

	public void installPackage(String baseUrl, String adminLogin,
			String adminPassword, File packageSource, String ftpLogin,
			String ftpPassword) throws Exception {
		login(baseUrl, adminLogin, adminPassword);
		installComponent(baseUrl, packageSource, ftpLogin, ftpPassword);
	}

	public void deleteComponent(String baseUrl, String extensionName,
			String extensionType, String adminLogin, String adminPassword,
			String ftpLogin, String ftpPassword) throws Exception {
		login(baseUrl, adminLogin, adminPassword);
		deleteExtension(baseUrl, extensionName, extensionType, ftpLogin,
				ftpPassword);
	}

	private void installComponent(String baseUrl, File archieveFile,
			String ftpUser, String ftpPassword) throws Exception {

		HttpGet httpget = new HttpGet(this.baseUrl
				+ "/administrator/index.php?option=com_installer");
		HttpResponse response = httpclient.execute(httpget);

		HttpEntity entity = response.getEntity();
		String body = EntityUtils.toString(entity);
		updateToken(body);

		HttpPost httpost = new HttpPost(this.baseUrl
				+ "/administrator/index.php");
		MultipartEntity reqEntity = new MultipartEntity();
		reqEntity.addPart("install_package", new FileBody(archieveFile));
		reqEntity.addPart(token, new StringBody("1"));
		reqEntity.addPart("installtype", new StringBody("upload"));
		reqEntity.addPart("task", new StringBody("doInstall"));
		reqEntity.addPart("option", new StringBody("com_installer"));
		reqEntity.addPart("username", new StringBody(ftpUser));
		reqEntity.addPart("password", new StringBody(ftpPassword));

		httpost.setEntity(reqEntity);

		response = httpclient.execute(httpost);
		entity = response.getEntity();
		body = EntityUtils.toString(entity);
		// System.out.println(body);
		// check for errors
		Pattern pattern = Pattern.compile(
				"<dd class=\"error message fade\">\\s*<ul>\\s*<li>(.*?)</li>",
				Pattern.MULTILINE);
		Matcher matcher = pattern.matcher(body);
		if (matcher.find()) {
			throw new IllegalStateException(matcher.group(1));
		}

	}

	private void deleteExtension(String baseUrl, String componentName,
			String extensionType, String ftpUser, String ftpPassword)
			throws Exception {
		String componentID = getComponentID(componentName, extensionType);
		if (componentID == null) {
			throw new IllegalStateException("Extension " + componentName
					+ " was not found");
		}
		HttpPost httpost = new HttpPost(this.baseUrl
				+ "/administrator/index.php");
		List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		nvps.add(new BasicNameValuePair(token, "1"));
		nvps.add(new BasicNameValuePair("option", "com_installer"));
		nvps.add(new BasicNameValuePair("task", "remove"));
		nvps.add(new BasicNameValuePair("type", extensionType + "s"));
		nvps.add(new BasicNameValuePair("username", ftpUser));
		nvps.add(new BasicNameValuePair("password", ftpPassword));
		if ("component".equals(extensionType)) {
			nvps.add(new BasicNameValuePair("eid", componentID));
		} else {
			nvps.add(new BasicNameValuePair(componentID, "0"));
		}

		httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
		HttpResponse response = httpclient.execute(httpost);

		String body = EntityUtils.toString(response.getEntity());
		Pattern pattern = Pattern.compile(
				"<dd class=\"error message fade\">\\s*<ul>\\s*<li>(.*?)</li>",
				Pattern.MULTILINE);
		Matcher matcher = pattern.matcher(body);
		if (matcher.find()) {
			throw new IllegalStateException(matcher.group(1));
		}

	}

	protected String token = "";
	protected String baseUrl = "";
	protected DefaultHttpClient httpclient;

	protected boolean login(String url, String user, String password)
			throws Exception {
		baseUrl = url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
		httpclient = new DefaultHttpClient();

		HttpGet httpget = new HttpGet(baseUrl + "/administrator/index.php");

		HttpResponse response;
		response = httpclient.execute(httpget);

		HttpEntity entity = response.getEntity();

		// EntityUtils.consume(entity);
		String body = EntityUtils.toString(entity);
		updateToken(body);

		HttpPost httpost = new HttpPost(baseUrl + "/administrator/index.php");

		List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		nvps.add(new BasicNameValuePair("option", "com_login"));
		nvps.add(new BasicNameValuePair("task", "login"));
		nvps.add(new BasicNameValuePair("username", user));
		nvps.add(new BasicNameValuePair("passwd", password));
		nvps.add(new BasicNameValuePair(token, "1"));

		httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
		response = httpclient.execute(httpost);
		entity = response.getEntity();
		if (entity != null) {

			body = EntityUtils.toString(entity);
			if (body.indexOf("Edit User") < 0) {
				throw new IllegalStateException("Unable to login to " + baseUrl
						+ "/administrator/index.php");
			}
		}

		return true;
	}

	private String getComponentID(String name, String type)
			throws ClientProtocolException, IOException {
		HttpPost httpost = new HttpPost(baseUrl + "/administrator/index.php");
		List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		nvps.add(new BasicNameValuePair(token, "1"));
		nvps.add(new BasicNameValuePair("option", "com_installer"));
		nvps.add(new BasicNameValuePair("task", "manage"));
		nvps.add(new BasicNameValuePair("type", type + "s"));
		nvps.add(new BasicNameValuePair("limit", "0"));
		nvps.add(new BasicNameValuePair("limitstart", "0"));

		httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
		HttpResponse response = httpclient.execute(httpost);

		HttpEntity entity = response.getEntity();
		String body = EntityUtils.toString(entity);
		updateToken(body);
		Pattern p = "component".equals(type) ? Pattern
				.compile(
						"<input\\s*type=\"[a-z]+\" id=\"cb[0-9]+\" "
								+ "name=\"eid\" value=\"([0-9]+)\" "
								+ "onclick\\s*=\\s*\"isChecked\\(this\\.checked\\);\"[\\s]*/>[\\s]*"
								+ "<span\\s*class\\s*=\\s*\"bold\">\\s*" + name
								+ "\\s*</span>", Pattern.MULTILINE)
				: Pattern
						.compile(
								"<input type=\"checkbox\"[^>]*name=\"([^\"]*)\"[^>]*/>\\s*<span\\s*class\\s*=\\s*\"bold\">\\s*"
										+ name + "\\s*</span>",
								Pattern.MULTILINE);
		Matcher m = p.matcher(body);
		String cId = null;
		while (m.find()) {
			cId = m.group(1);
		}
		return cId;
	}

	protected void updateToken(String body) {
		Pattern p = Pattern.compile(
				"<input type=\"hidden\" name=\"([a-z0-9]+)\" value=\"1\" />",
				Pattern.MULTILINE);
		Matcher m = p.matcher(body);
		while (m.find()) {
			token = m.group(1);
		}
	}
}
